/*
 * Copyright (c) 2016 Qunar.com. All Rights Reserved.
 */
package com.guoyu.project.common.context;

import java.io.Closeable;
import java.io.IOException;
import java.util.EnumMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author: libo Date: 16-3-3 Time: 下午6:08
 * @version: \$Id$
 **/
public class CurrentContext {

    private static Logger log = LoggerFactory.getLogger(CurrentContext.class);

    /**
     * 当前线程内的实例
     */
    static ThreadLocal<CurrentContext> currentContext = new ThreadLocal<CurrentContext>();

    /**
     * name => instance
     */
    private final EnumMap<ContextKey, Object> cache = new EnumMap<ContextKey, Object>(ContextKey.class);

    /**
     * @return 当前线程绑定的实例，如果尚未创建，创建之
     */
    public static CurrentContext getCurrent() {
        CurrentContext context = currentContext.get();
        if (context == null) {
            currentContext.set(context = new CurrentContext());
        }
        return context;
    }

    /**
     * 往当前线程绑定的上下文实例中注入对应类型的对象
     * @param key
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> T setToCurrent(ContextKey key, T obj){
        CurrentContext context = getCurrent();
        return (T) context.set(key, obj);
    }

    /**
     * 从当前线程绑定的上下文实例中按照类型（必须精确匹配）获取已注入的对象
     *
     * @param key
     * @return 对象
     */
    public static <T> T getFromCurrent(ContextKey key) {
        CurrentContext context = getCurrent();
        Object object = context.get(key);
        if (object == null) {
            return null;
        }
        return (T) key.getClazz().cast(object);
    }

    /**
     * @param key
     * @return 如果当前上下文中有类名对应（必须精确匹配）的对象，返回；否则，返回null
     */
    private Object get(ContextKey key) {
        return cache.get(key);
    }

    /**
     * 向上下文中注入对象
     *
     * @param key
     * @param object
     * @return
     */
    private Object set(ContextKey key, Object object) {
        return cache.put(key, object);
    }

    /**
     * 从上下文中移除对象,如果注入的对象为 @see {@link Closeable} 的实现类，将自动调用其 @see {@link Closeable#close()} 方法
     *
     * @param contextKey
     * @return 移除的对象（对于 @see {@link Closeable} 的实现类，由于已经调用关闭方法，则不应该继续尝试调用其关闭方法）
     */
    public Object remove(ContextKey contextKey) {
        Object removed = cache.remove(contextKey);
        if (removed != null) {
            if (removed instanceof Closeable) {
                try {
                    ((Closeable) removed).close();
                } catch (IOException e) {
                    log.error("close上下文中的对象异常", e);
                }
            }
        }
        return removed;
    }

    /**
     * 清除所有已注入的对象，如果注入的对象为 @see {@link Closeable} 的实现类，将自动调用其 @see {@link Closeable#close()} 方法
     */
    public void clear() {
        for (Map.Entry<ContextKey, Object> entry : cache.entrySet()) {
            if (entry.getValue() instanceof Closeable) {
                try {
                    ((Closeable) entry.getValue()).close();
                } catch (IOException e) {
                    log.error("close上下文中的对象异常", e);
                }
            }
        }
        cache.clear();
    }

    /**
     * 关闭上下文对象，自动清除所有已注入的对象，如果注入的对象为 @see {@link Closeable} 的实现类，将自动调用其 @see {@link Closeable#close()} 方法
     */
    public void close() {
        clear();
        currentContext.set(null);
        currentContext.remove();
    }
    
}
